package com.neuronbit.businessflow.flow.source;

import com.neuronbit.businessflow.flow.BpmFile;
import com.neuronbit.businessflow.flow.parser.FlowNode;
import com.neuronbit.businessflow.flow.project.Project;
import com.neuronbit.businessflow.flow.source.util.SourceContext;
import com.neuronbit.businessflow.flow.source.util.StepJavaSourceFactory;
import com.neuronbit.businessflow.intellij.model.Node;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * The type Bpm java source.
 */
public class BpmJavaSource {
    private final FlowNode flow;
    private final BpmFile bpmFile;
    private final String packageName;
    private final Project project;
    private final List<JavaSource> javaSources = new ArrayList<>();

    /**
     * Instantiates a new Bpm java source.
     *
     * @param bpmFile the bpm file
     */
    public BpmJavaSource(BpmFile bpmFile) {
        this.bpmFile = bpmFile;
        this.packageName = bpmFile.getPackageName();
        this.project = bpmFile.getProject();
        this.flow = bpmFile.getFlow();
    }

    /**
     * Generate.
     *
     * @throws IOException            the io exception
     * @throws ClassNotFoundException the class not found exception
     * @throws NoSuchMethodException  the no such method exception
     */
    public void generate() throws IOException, ClassNotFoundException, NoSuchMethodException {
        final ContextJavaSource contextSource = new ContextJavaSource(flow.getName(), this.packageName);
        final FlowParamJavaSource flowParamSource = new FlowParamJavaSource(flow, contextSource, this.packageName, this.project);
        final FlowJavaSource flowNodeSource = new FlowJavaSource(flow, contextSource, flowParamSource);

        final SourceContext sourceContext = new SourceContext(packageName, flowNodeSource, contextSource, flowParamSource, project, flow);

        for (Node node : flow.nodes()) {
            //step will contribute statements into flow or context, add them first
            JavaSource nodeSource = StepJavaSourceFactory.createNodeSource(node, sourceContext);
            javaSources.add(nodeSource);
            if (nodeSource instanceof FLowEntryStep) {
                flowNodeSource.setEntryStep((FLowEntryStep) nodeSource);
            }
        }

        javaSources.add(flowParamSource);
        javaSources.add(flowNodeSource);
        //add context last
        javaSources.add(contextSource);

        for (JavaSource javaSource : javaSources) {
            javaSource.generate();
            javaSource.writeJavaSourceFile(this.project.getOutputPath());
        }
    }
}
